﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using MoneyWentWhere.DataModel.Binding;
using System.Collections;
using MoneyWentWhere.DataModel.Tx;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace MoneyWentWhere.DataModel
{
    [TypeConverter(typeof(TxOmniTypeConverter))]
    public class TxOmni
    {
        public const String TransferPrefix = "@";
        public const String TagPrefix = "#";

        public enum TokenTypeT { Tag, TransferAcct, Info }

        internal class Token
        {
            public TokenTypeT TokenType;
            public String Text;

            // start position of the token with the main string
            public int Position;

        }


        String FullText;
        List<Token> Tokens = new List<Token>();

        public static TxOmni Parse(String rep)
        {
            var o = new TxOmni();

            o.FullText = rep + " &";

            // Parse the tokens


            return o;
        }

        void AddToken(StringBuilder sb, TokenTypeT type, String text)
        {
            // Dividing the tokens
            if (sb.Length > 0) { sb.Append(", "); }

            Tokens.Add(new Token { TokenType = type, Text = text, Position = sb.Length });
            sb.Append(text);
        }

        public String ToRepString()
        {
            return FullText;
        }

        internal static TxOmni Create(Transaction t)
        {
            var o = new TxOmni();
            var sb = new StringBuilder();

            // Add transfer info
            if (t.TransferToTx != null)
            {
                o.AddToken(sb, TokenTypeT.TransferAcct,
                    TransferPrefix + t.TransferToTx.Account.Name);

                // Info on amount if in different currency
                if (t.Account.Currency != t.TransferToTx.Account.Currency)
                {
                    o.AddToken(sb, TokenTypeT.Info,
                        String.Format("({0:0} {1})", Math.Abs(t.TransferToTx.Amount), t.TransferToTx.Account.Currency));
                }
            }

            // Add reconcile info
            if (t.RecoBalance != null)
            {
                // Find previous reco
                var prevReco = t.FindPreviousReco();
                var recoStartDate = (prevReco ?? t.Account.Transactions.First()).Date;

                if (recoStartDate.Date != t.Date.Date)
                {
                    TimeSpan timeDiff = t.Date - recoStartDate.Date;

                    decimal amountPerDay = t.Amount / (decimal)timeDiff.TotalDays;

                    o.AddToken(sb, TokenTypeT.Info,
                        String.Format("Reconcile ({0}{1:#0}/day, {2:0.#} days)",
                            amountPerDay > 0 ? "+" : "",
                            Math.Abs(amountPerDay),
                            timeDiff.TotalDays));
                }
                else
                {
                    o.AddToken(sb, TokenTypeT.Info, "Reconcile (same day)");
                }
            }

            // Add tags
            if (t.Tags.Any())
            {
                foreach (String tag in t.Tags)
                {
                    o.AddToken(sb, TokenTypeT.Tag, tag);
                }
            }

            // Temporary addition of category as tags
            if (!String.IsNullOrEmpty(t.Category))
            {
                foreach (String cat in t.Category.Split('/'))
                {
                    o.AddToken(sb, TokenTypeT.Tag, "/" + cat);
                }
            }

            o.FullText = sb.ToString();

            return o;
        }

        internal void Apply(Transaction t)
        {
            //t.Comment = OriginalString;
        }

        #region Helpers
        internal static int IndexOfMinLengthItem(IList list, out int minLength)
        {
            minLength = int.MaxValue;
            int index = -1;
            for (int i = 0; i < list.Count; i++)
            {
                String item;
                if (list[i] is Account) { item = ((Account)list[i]).Name; }
                else { item = (String)list[i]; }

                int len = item.Length;
                if (len < minLength)
                {
                    index = i;
                    minLength = len;
                }
            }
            return index;
        }

        internal static bool TryExtractNumber(String str, out decimal value)
        {
            value = 0;
            Match m = Regex.Match(str, @"[0-9,.]+");
            if (m.Groups.Count == 0) { return false; }
            return decimal.TryParse(m.Groups[0].Value, out value);
        }

        #endregion 
    }
}
